package com.totem.base.filter;


import cn.hutool.core.codec.Base64;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.RSA;
import cn.hutool.crypto.symmetric.AES;
import cn.hutool.http.Method;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.totem.base.SpringContextHolder;
import com.totem.base.constants.CommonConstants;
import com.totem.base.constants.ErrorCode;
import com.totem.base.constants.SecurityComponets;
import com.totem.base.exception.BaseException;
import com.totem.base.filter.support.ModifyRequestWrapper;
import com.totem.base.filter.support.ModifyResponseWrapper;
import com.totem.base.util.FilterUtil;
import com.totem.base.util.RequestUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.context.ApplicationContext;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Set;

@Slf4j
public class SecurityFilter extends OncePerRequestFilter {
    private static final AntPathMatcher ignoresMatch = new AntPathMatcher();
    private ObjectMapper objectMapper;
    private RSA rsa = SecurityComponets.DEFAULT_ENCRYPTOR;
    private Set<String> ignores = Set.of(
            "/",
            "/swagger-ui.html",
            "/swagger-ui/",
            "/doc.html",
            "/favicon.ico",
            "/**/*.html",
            "/**/*.css",
            "/**/*.js",
            "/swagger-resources/**",
            "/v3/api-docs/**",
            "/admin/test/**",
            "/admin/config/system"
    );

    @Override
    protected void initFilterBean() throws ServletException {
        ApplicationContext ctx = SpringContextHolder.getApplicationContext();
        objectMapper = ctx.getBean("objectMapper", ObjectMapper.class);
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        String requestUri = request.getRequestURI();
        if (log.isDebugEnabled()) {
            log.debug("requestUri:{}", requestUri);
        }

        Long enterTime = System.currentTimeMillis();

        AES aes = null;
        String decryptedSign = null;
        if (CommonConstants.ENCRYPT_ENABLE) {
            String encryptedKey = request.getHeader(CommonConstants.SYMMETRY_HEADER_CRYPTO);
            String encryptedIv = request.getHeader(CommonConstants.SYMMETRY_HEADER_IV);
            String encryptedSign =request.getHeader(CommonConstants.SYMMETRY_HEADER_SIGN);
            if (StringUtils.isBlank(encryptedKey) || StringUtils.isBlank(encryptedIv) || StringUtils.isBlank(encryptedSign)) {
                log.error("Key, iv or sign is empty. key:{}, iv:{}, sign:{}", encryptedKey, encryptedIv, encryptedSign);
                throw new BaseException(ErrorCode.BAD_REQUEST);
            }
            String key = rsa.decryptStr(encryptedKey, KeyType.PrivateKey, CharsetUtil.CHARSET_UTF_8);
            String iv = rsa.decryptStr(encryptedIv, KeyType.PrivateKey, CharsetUtil.CHARSET_UTF_8);
            if(log.isDebugEnabled()) {
                log.debug("key:{}, iv:{}", key, iv);
            }
            //byte[] keyBytes = Base64.decode(key);
            //byte[] ivBytes = Base64.decode(key);
            byte[] keyBytes = key.getBytes(StandardCharsets.UTF_8);
            byte[] ivBytes = iv.getBytes(StandardCharsets.UTF_8);
            log.debug("{}, {}", keyBytes.length, ivBytes.length);
            aes = new AES("CBC", "PKCS7Padding", keyBytes, ivBytes);
            // decryptedSign = aes.decryptStr(encryptedSign);
            ModifyRequestWrapper modifyRequestWrapper = new ModifyRequestWrapper(request, aes);
            ModifyResponseWrapper modifyResponseWrapper = new ModifyResponseWrapper(response, aes, objectMapper);
            try {
                filterChain.doFilter(modifyRequestWrapper, modifyResponseWrapper);
            } finally {
                modifyResponseWrapper.updateContent();
            }
        }else {
            // //按Order优先顺序调用下一个Filter，直到Controller层
            filterChain.doFilter(request, response);
        }
    }

    @Override
    protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException {
        if(FilterUtil.ignoreMethod(request)) return true;

        final String requestUri = RequestUtil.getRelativePath(request);

        return ignores.stream().anyMatch(e->ignoresMatch.match(e, requestUri))
                || !StringUtils.equalsAnyIgnoreCase(request.getMethod(), Method.POST.name(), Method.GET.name());
    }
}
